package com.itfreer.gis.server.vector.generate;

import com.itfreer.gis.server.mercator.TileUtils;
import com.itfreer.gis.server.vector.Pos;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.geotools.geometry.jts.ReferencedEnvelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.springframework.stereotype.Component;

/**
 * 矢量切片地图输出格式化 根据地图范围，查询要素，并生成切片
 */
@Component
public class VectorTileGenerate {

	private VectorTileBuilderFactory tileBuilderFactory;

	public void setTileBuilderFactory(VectorTileBuilderFactory tileBuilderFactory) {
		this.tileBuilderFactory = tileBuilderFactory;
	}

	/**
	 * 地图切片
	 * 
	 * @param source   数据源
	 * @param extent   切片范围，如：{ 117, 26, 123, 32 }
	 * @param minLevel 最小级别
	 * @param maxLevel 最大级别
	 * @throws Exception
	 */
	public byte[] generate(String source, CoordinateReferenceSystem sourceCrs, List<Map<String, Object>> features,
			int x, int y, int z) throws Exception {
		// 3.单个切片
		final ReferencedEnvelope renderingArea = TileUtils.parseXyz2Bound(x, y, z, sourceCrs);
		VectorTileBuilder vectorTileBuilder = this.tileBuilderFactory.newBuilder();
		Pipeline pipeline = getPipeline(renderingArea, z);
		int count = run(source, x, y, z, features, pipeline, vectorTileBuilder);
		if (count > 0) {
			return vectorTileBuilder.build();
		} else {
			return null;
		}
	}

	protected Pipeline getPipeline(final ReferencedEnvelope renderingArea, int z) throws FactoryException {
		final PipelineBuilder builder = PipelineBuilder.newBuilder(renderingArea);
		return builder.simplify(z).clip().collapseCollections().build();
	}

	protected int run(String source, int x, int y, int z, List<Map<String, Object>> features, Pipeline pipeline,
			VectorTileBuilder vectorTileBuilder) throws ParseException {
		int count = 0;
		if (features != null && features.size() > 0) {
			String wkt = (String) features.get(0).get("geom");
			Geometry geom = new WKTReader().read(wkt);
			if ((geom instanceof Point) || (geom instanceof MultiPoint)) {
				features = posPoint(z, features);
			}

			for (Map<String, Object> m : features) {
				wkt = (String) m.get("geom");
				Geometry originalGeom = new WKTReader().read(wkt);
				m.remove("geom");

				try {
					originalGeom = pipeline.execute(originalGeom);
				} catch (Exception processingException) {
					processingException.printStackTrace();
					continue;
				}
				if (originalGeom.isEmpty()) {
					continue;
				}

				final long featureId = Long.parseLong(m.get("gid").toString());
				TileUtils.convert2Piexl(x, y, z, originalGeom);
				vectorTileBuilder.addFeature(source, featureId, originalGeom, m);
				count++;
			}
		}
		return count;
	}

	/**
	 * 点抽稀
	 * 
	 * @param features
	 * @throws ParseException
	 */
	protected List<Map<String, Object>> posPoint(int z, List<Map<String, Object>> features) throws ParseException {
		if (z >= 16) {
			return features;
		}
		
		int dis = 0;
		switch (z) {
		case 9:
			dis = 250;
			break;
		case 10:
			dis = 250;
			break;
		case 11:
			dis = 250;
			break;
		case 12:
			dis = 250;
			break;
		case 13:
			dis = 250;
			break;
		case 14:
			dis = 200;
			break;
		case 15:
			dis = 150;
			break;
		}
		double degToMeter = Math.PI * 6378137 / 180.0; // 6378137赤道半径，一度对应赤道上的一米
		int buf = (int) (dis * 1.0e7 / degToMeter); // 1公里对应多少度
		List<Map<String, Object>> result = new ArrayList<>();
		List<Pos> spector = new LinkedList<Pos>();
		for (Map<String, Object> m : features) {
			String wkt = (String) m.get("geom");
			Geometry originalGeom = new WKTReader().read(wkt);
			int x = (int) (originalGeom.getCoordinate().x * 1e7);
			int y = (int) (originalGeom.getCoordinate().y * 1e7);
			Pos cur = new Pos(x, y);
			cur.setBuffer(buf);
			if (!spector.contains(cur)) {
				spector.add(cur);
				result.add(m);
			}
		}
		return result;
	}
}
